/*
 Copyright (C) 2007 - Michael Opitz
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/
#include "my_ssl.h"

static int init = 0;

SSL_CTX* init_ctx()
{
	SSL_METHOD *method;
	SSL_CTX *ctx;
	OpenSSL_add_all_algorithms();
	SSL_load_error_strings();
	method = TLSv1_client_method();
	ctx = SSL_CTX_new(method);
	if(ctx == NULL) {
		ERR_print_errors_fp(stderr);
		return NULL;
	}
	return ctx;
}

ssl_system* init_ssl()
{
	if(init == 0) {
		init = 1;
		SSL_library_init();
	} else {
		init = init + 1;
	}
	ssl_system *sys = (ssl_system*) malloc(sizeof(ssl_system));
	sys->_ctx = init_ctx();
	if(sys->_ctx == NULL) {
		ERR_remove_state(0);
		ERR_free_strings();
		EVP_cleanup();
		free(sys);
		return NULL;
	}
	return sys;
}

void free_ssl(ssl_system *sys)
{
	assert(sys != NULL);
	SSL_CTX_free(sys->_ctx);
	ERR_remove_state(0);
	ERR_free_strings();
	EVP_cleanup();
	free(sys);
}

void close_socket(ssl_system *sys, ssl_socket *sock)
{
	assert(sys != NULL && sock != NULL);
	SSL_free(sock->_ssl);
	close(sock->_sock);
	free(sock);
}

ssl_socket* open_socket(ssl_system *sys, const char *hostname, int port)
{
	assert(sys != NULL);

	SSL *ssl;
	int sd;
	struct hostent *host;
	struct sockaddr_in addr;
	if((host = gethostbyname(hostname)) == NULL) {
		perror(hostname);
		return NULL;
	}
	sd = socket(PF_INET, SOCK_STREAM, 0);
	bzero(&addr, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = *(long*)(host->h_addr);
	if(connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
		close(sd);
		perror(hostname);
		return NULL;
	}

	ssl = SSL_new(sys->_ctx);
	SSL_set_fd(ssl, sd);
	if(SSL_connect(ssl) == -1) {
		ERR_print_errors_fp(stderr);
		close(sd);
		return NULL;
	}
	
	ssl_socket *sock = (ssl_socket*) malloc(sizeof(ssl_socket));
	sock->_sock = sd;
	sock->_ssl = ssl;
	return sock;
}

void write_sock(ssl_system *sys, ssl_socket *sock, const char *msg)
{
	assert(sys != NULL && sock != NULL);
	SSL_write(sock->_ssl, msg, strlen(msg));
}

int read_sock(ssl_system *sys, ssl_socket *sock, char *buff, int size)
{
	assert(sys != NULL && sock != NULL);
	return SSL_read(sock->_ssl, buff, size);
}

